<?php

namespace Common\Controller;

/**
 * 文件上传控制器
 *
 * @since 1.0 2014-11-7 by sutroon
 * @since 2.0 <2015-7-16> SoChishun 新增const字段,重构所有方法
 */
class UploadHandlerController {

    const CATEGORY_ATTACHMENT = 'Attachment';
    const CATEGORY_DOWNLOADS = 'Downloads';
    const CATEGORY_EXPORT = 'Export';
    const CATEGORY_FACE = 'Face';
    const CATEGORY_IMAGES = 'Images';
    const CATEGORY_IMPORT = 'Import';
    const CATEGORY_PHOTO = 'Photo';
    const CATEGORY_PRODUCT = 'Product';
    const CATEGORY_THUMBNAIL = 'Thumbnail';
    const CATEGORY_USER = 'User';

    /**
     * kindeditor编辑器上传处理
     * <br />512000=500KB
     * <br />2097152=2MB
     * @param string $category 分类
     * @param boolean|array $exdb 是否保存到数据库(若为数组则表示附加到数据库的扩展数据)
     * @since 1.0 <2014-11-7> sutroon Added.
     * @since 2.0 <2015-3-28> SoChishun 重构.
     * @since 2.1 <2015-7-16> SoChishun 重构.
     */
    public function kindeditor_upload($category = 'Attachment') {
        $msg = self::upload(array('savePath' => $category . '/', 'maxSize' => 512000, 'skipEmpty' => true));
        if (is_array($msg)) {
            $msg = current($msg);
            $result = array('error' => 0, 'url' => $msg['filepath']);
        } else {
            $result = array('error' => 1, 'message' => $msg);
        }
        $this->ajaxReturn($result);
    }

    /**
     * 上传文件
     * <br />注意：请确保目录存在(不会自动创建目录)
     * @param array $options 选项 
     * <br /> 'rootPath' => './Uploads/', // 保存根路径
     * <br />'savePath' => '', // 保存次目录,如 Images/或Faces/
     * <br />'autoSub' => true, // 自动子目录保存文件
     * <br />'subName' => array('date', 'Y-m-d'), // 子目录创建方式，[0]-函数名，[1]-参数，多个参数使用数组
     * <br />'saveName' => array('uniqid', ''), // 上传文件命名规则，[0]-函数名，[1]-参数，多个参数使用数组, 空数组表示保持原名,字符串表示新文件名(无后缀)
     * <br />'saveExt' => '', // 文件保存后缀，空则使用原后缀
     * <br />'replace' => true, // 存在同名是否覆盖,false则提示存在同名文件
     * <br /> 'maxSize' => 2097152, // 上传的文件大小限制 (0-不做限制,2097152=2MB,512000=500KB,204800=200KB)
     * <br />'exts' => array('gif', 'jpg', 'jpeg', 'bmp', 'png'),  //允许上传的文件后缀
     * <br />'skipEmpty' => true // 是否过滤空文件,防止表单无文本域出错
     * @param array $files 文件,如果为''则默认为$_FILES
     * @return string|array 失败则返回错误字符串,成功则返回二维数组:array('文件域名称'=>array('filepath'=>'文件保存的具体路径','savename'=>'文件名','size'=>'文件大小','type'=>'文件MiMe类型','ext'=>'文件后缀'))
     * @since 1.0 <2015-3-27> SoChishun Added.
     * @since 1.1 <2015-7-16> SoChishun 完善注释.
     * @example 取值filepath,savename
     *      上传到子目录：
      $msg = upload(array('savePath' => 'Import/', 'exts' => array('xlsx', 'xls', 'txt'), 'autoSub' => true, 'subType' => 'date', 'saveName' => array(), 'replace' => TRUE));
      if (!is_array($msg)) {
      return $this->returnMsg(false, $msg);
      }
     *      上传商品
     *      $files = upload(array('skipEmpty' => true,'savePath'=>'Product/'));
      if (is_array($files)) {
      $this->thumb_url = $files['thumb_url']['filepath'];
      }
     *      $msg=upload(array('savePath'=>'Product/','maxSize'=>512000,'skipEmpty'=>true));
     */
    static function upload($options = array(), $files = '', $suboptions) {
        set_time_limit(0);
        if (!$files) {
            $files = $_FILES;
        }
        // 过滤空文件,防止出错
        if (isset($options['skipEmpty']) && $options['skipEmpty']) {
            if ($options['skipEmpty']) {
                $files = self::file_filter($emptys, $files);
                if (!$files) {
                    return '没有文件被上传!';
                }
            }
            unset($options['skipEmpty']);
        }
        $config = array(
            'rootPath' => './Uploads/', // 保存根路径
            'savePath' => '', // 保存次目录,如 Images/或Faces/
            'autoSub' => true, // 自动子目录保存文件
            'subName' => array('date', 'Y-m-d'), // 子目录创建方式，[0]函数名，[1]-参数，多个参数使用数组
            'saveName' => array('uniqid', ''), // 上传文件命名规则，[0]函数名，[1]-参数，多个参数使用数组, 空数组表示保持原名,字符串表示新文件名(无后缀)
            'saveExt' => '', // 文件保存后缀，空则使用原后缀
            'replace' => true, // 存在同名是否覆盖,false则提示存在同名文件
            'maxSize' => 0, // 上传的文件大小限制 (0-不做限制,2097152=2MB,512000=500KB,204800=200KB)
            'exts' => array('g729', 'mp3', 'wav', 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'txt', 'zip', 'rar', 'doc', 'docx', 'xls', 'xlsx', 'apk', 'ipa'), //允许上传的文件后缀
            'hash' => false, // 是否生成hash编码
            'mimes' => array(), // 允许上传的文件MiMe类型
        );
        $config = array_merge($config, $options);
        is_dir($config['rootPath']) || mkdir($config['rootPath']); // 自动创建上传根目录
        $uploader = new \Think\Upload($config);
        $infos = $uploader->upload($files);
        if ($uploader->getError()) {
            return $uploader->getError();
        }
        // 新增一个filepath合成路径
        $returns = array();
        foreach ($infos as $key => $file) {
            $file['filepath'] = ltrim($config['rootPath'], '.') . $file['savepath'] . $file['savename'];
            $returns[$key] = $file;
        }
        return $returns;
    }

    /**
     * 生成缩略图
     * @param string $path
     * @param string $msg
     * @param array $options
     * @return boolean
     * @since 1.0 <2015-11-02> SoChishun Added.
     */
    static function save_thumb($path, &$msg = '', $options = array()) {
        $config = array('PREFIX' => 's_', 'WIDTH' => 180, 'HEIGHT' => 120);
        if ($options) {
            $config = array_merge($config, $options);
        }
        if (IS_WIN) {
            // 解决Window下不支持中文 2016-1-20 SoChishun Fix.
            // See:http://www.oschina.net/question/36370_13303
            $path = iconv('utf-8', 'gbk', $path);
        }
        $abs_path = sofn_path_rtoa($path);
        if (!file_exists($abs_path)) {
            $msg = '路径[' . $path . ']不是有效文件!';
            return false;
        }
        $ext = pathinfo($path, PATHINFO_EXTENSION);
        $exts = array('jpg', 'gif', 'jpeg', 'png');
        if (!$ext || !in_array(strtolower($ext), $exts)) {
            $msg = '不是有效图像!';
            return false;
        }
        $thumb_path = sofn_rename_file($path);
        $img = new \Think\Image();
        $img->open($abs_path);
        $img->thumb($config['WIDTH'], $config['HEIGHT']);
        $img->save(sofn_path_rtoa($thumb_path));
        $msg = $thumb_path;
        return true;
    }

    /**
     * 过滤上传文件中的无效文件
     * @param array|false $emptys 无效文件名数组
     * @param array $files 文件,一般是$_FILES
     * @return array 返回过滤后的有效文件数组
     * @since 1.0 <2015-3-27> SoChishun Added.
     */
    private static function file_filter(&$emptys, $files = '') {
        if ('' === $files) {
            $files = $_FILES;
        }
        $emptys = false;
        foreach ($files as $name => $file) {
            if ($file['error'] > 0) {
                $emptys[] = $name;
            }
        }
        if ($emptys) {
            foreach ($emptys as $name) {
                unset($files[$name]);
            }
        }
        return $files;
    }

    /**
     * 上传文件(支持一个或多个文件上传),并保存到数据库
     * <br />注意：请确保目录存在(不会自动创建目录)
     * @param array $options 上传配置
     * <br /> 'rootPath' => './Uploads/', // 保存根路径
     * <br />'savePath' => '', // 保存次目录,如 Images/或Faces/
     * <br />'autoSub' => true, // 自动子目录保存文件
     * <br />'subName' => array('date', 'Ymd'), // 子目录创建方式，[0]-函数名，[1]-参数，多个参数使用数组
     * <br />'saveName' => array('uniqid', ''), // 上传文件命名规则，[0]-函数名，[1]-参数，多个参数使用数组, 空数组表示保持原名,字符串表示新文件名(无后缀)
     * <br />'saveExt' => '', // 文件保存后缀，空则使用原后缀
     * <br />'replace' => true, // 存在同名是否覆盖,false则提示存在同名文件
     * <br /> 'maxSize' => 2097152, // 上传的文件大小限制 (0-不做限制,2097152=2MB,512000=500KB,204800=200KB)
     * <br />'exts' => array('gif', 'jpg', 'jpeg', 'bmp', 'png'),  //允许上传的文件后缀
     * <br />'skipEmpty' => true // 是否过滤空文件,防止表单无文本域出错
     * @param boolean|array $exdb 是否保存到数据库(如为数组则表示附加到数据库的字段值)
     * @return string|array 失败则返回错误字符串,成功则返回二维数组:array('文件域名称'=>array(fileName,filePath,fileExt,fileSize,fileID))
     * @example 取值：fileName, filePath
     *      上传到子目录
     *      $msg = sofn_upload_handle(array('savePath' => $category . '/', 'maxSize' => 512000, 'skipEmpty' => true), $exdb);
      if (is_array($msg)) {        }
     *      上传到其他绝对目录,禁止自动生成子目录,并指定文件名：
     *      sofn_upload_handle(array('rootPath' => '/var/lib/asterisk/moh/xcall_1_nu2/', 'autoSub' => false, 'exts' => $allowExts, 'saveName' => 'orig_' . $fnotext), array('dirName' => '', 'exTags' => 'moh', 'fileName' => 'wav_liantong.wav', 'filePath' => '/var/lib/asterisk/moh/xcall_1_nu2/wav_liantong.wav'));
     *      上传到其他绝对目录,禁止自动生成子目录
     *      $msg = sofn_upload_handle(array('rootPath'=>$_SERVER['DOCUMENT_ROOT'] . '/','savePath' => 'xcallsqzs/Licence/', 'saveName' => 'licence','autoSub'=>false));
     * @since 1.0 2014-6-30 by sutroon
     * @since 1.1 2014-7-18 by sutroon 新增saveRule，将其他参数合并成$exData
     * @since 1.2 2014-8-30 by sutroon 将saveRule和$exData参数合并到$config参数中,并新增若干可定制上传参数,支持缩略图生成,支持自动生成子目录 (搞定了上传这个心病,几天来压抑的心情舒坦多了^_^)
     * @since 1.3 2014-10-21 by sutroon 修正有savePath子路径时无法自动创建完整路径的问题
     * @since 2.0 <2015-7-16> SoChishun 适配TP3.2和sofn_upload()方法进行重构,参数更合理,统一返回二维数组.
     */
    static function upload2db($options = array(), $exdb = array()) {
        $files = sofn_upload($options);
        if (!is_array($files)) {
            return $files;
        }
        $datas = array();
        foreach ($files as $name => $info) {
            $data = null;
            if (!is_array($exdb) || !isset($exdb['fileName'])) {
                $data['file_name'] = $info['savename']; // wangluoqingge.mp3
            }
            if (!is_array($exdb) || !isset($exdb['filePath'])) {
                $data['file_path'] = $info['filepath']; // /var/lib/asterisk/moh/xcall_13_002/wangluoqingge.mp3
            }

            $data['file_size'] = $info['size']; // 877769
            $data['file_type'] = $info['type']; // audio/mp3
            $data['file_ext'] = $info['ext']; // mp3
            if (false !== $exdb) {
                $table = 'db_xcall.tgeneral_file';
                if (isset($exdb['TABLE'])) {
                    $table = $exdb['TABLE'];
                    unset($exdb['TABLE']);
                }
                if (is_array($exdb)) {
                    $data = array_merge($data, $exdb);
                }
                $where['file_path'] = $data['file_path'];
                $M = M($table);
                $result = $M->where($where)->getField('id');
                if (!$result) {
                    $result = $M->add($data);
                    if (false === $result) {
                        return '数据库写入失败!' . $M->getDbError();
                    }
                }
                $data['file_id'] = $result;
            }
            $datas[$name] = $data;
        }
        return $datas;
    }

    /**
     * 上传语音文件到Asterisk
     * @param int $site_id 租户ID
     * @param string $type 只支持moh、sounds
     * @param string $dirname 子目录名称
     * @param string $fieldName 上传域名称 如：fileUrl
     * @return array array('success'=>bool,'message'=>error|fileName, 'data'=>array);
     * @since 1.0 2014-12-27 by sutroon
     */
    public static function uploadToAsterisk($site_id, $type, $dirname, $fieldName = 'fileUrl') {
        if ($type == 'moh') {
            $dir = '/var/lib/asterisk/moh';
            $allowExts = array('mp3', 'wav');
        } else {
            $dir = '/var/lib/asterisk/sounds';
            $allowExts = array('g729', 'mp3', 'wav');
        }
        if (!sofunc_start_with($dirname, 'xcall_')) {
            $dirname = 'xcall_' . $site_id . '_' . $dirname;
        }
        $dir .= '/' . $dirname . '/';
        if ($type == 'moh' && is_dir($dir)) {
            return array('success' => false, 'message' => '子目录已存在!');
        }

        if (isset($_FILES[$fieldName]['name'])) {
            return array('success' => false, 'message' => '未选择文件!');
        }
        $filename = $_FILES[$fieldName]['name'];
        if (strpos($filename, ' ') !== false) {
            return array('success' => false, 'message' => '文件名不能包含空格!');
        }
        $ext = substr($filename, strrpos($filename, '.') + 1);
        $fnotext = substr($filename, 0, strrpos($filename, '.')); // 无后缀的文件名
        if (!in_array($ext, $allowExts)) {
            return array('success' => false, 'message' => '文件格式不支持!');
        }
        $data['createdTime'] = now();
        $data['dirName'] = $dirname; // xcall_13_002
        $data['exTags'] = $type; // moh
        $data['site_id'] = $site_id;
        $data['fileName'] = 'wav_' . $fnotext . '.wav';
        $data['filePath'] = $dir . '/wav_' . $fnotext . '.wav';
        $msg = sofn_upload_handle($dir, array('exData' => $data, 'allowExts' => $allowExts, 'saveRule' => 'orig_' . $fnotext));
        if (!is_array($msg)) {
            return array('success' => false, 'message' => $msg);
        }
        return array('success' => false, 'message' => $msg['filePath'], 'data' => $msg);
    }

}
